module ysyx_23060189_Xbar #
(
  parameter addrwidth = 32,
  parameter datawidth = 32
)
(
  // Global
  input  wire                   ACLK,
  input  wire                   ARESETn,

  /* Xbar <=> Arbiter */
  // Write address channel
  input  wire                   AWVALID_SLAVE_1,
  output reg                    AWREADY_SLAVE_1,
  input  wire [addrwidth-1:0]   AWADDR_SLAVE_1,
  input  wire [2:0]             AWPROT_SLAVE_1,

  // Write data channel
  input  wire                   WVALID_SLAVE_1,
  output reg                    WREADY_SLAVE_1,
  input  wire [datawidth-1:0]   WDATA_SLAVE_1,
  input  wire [datawidth/8-1:0] WSTRB_SLAVE_1,

  // Write response channel
  output reg                    BVALID_SLAVE_1,
  input  wire                   BREADY_SLAVE_1,
  output reg  [1:0]             BRESP_SLAVE_1,

  // Read address channel
  input  wire                   ARVALID_SLAVE_1,
  output reg                    ARREADY_SLAVE_1,
  input  wire [addrwidth-1:0]   ARADDR_SLAVE_1,
  input  wire [2:0]             ARPROT_SLAVE_1,

  // Read data channel
  output reg                    RVALID_SLAVE_1,
  input  wire                   RREADY_SLAVE_1,
  output reg  [datawidth-1:0]   RDATA_SLAVE_1,
  output reg  [1:0]             RRESP_SLAVE_1,

  /* Xbar <=> DSRAM */
  // Write address channel
  output reg                    AWVALID_SLAVE_DSRAM,
  input  wire                   AWREADY_SLAVE_DSRAM,
  output reg  [addrwidth-1:0]   AWADDR_SLAVE_DSRAM,
  output reg  [2:0]             AWPROT_SLAVE_DSRAM,

  // Write data channel
  output reg                    WVALID_SLAVE_DSRAM,
  input  wire                   WREADY_SLAVE_DSRAM,
  output reg  [datawidth-1:0]   WDATA_SLAVE_DSRAM,
  output reg  [datawidth/8-1:0] WSTRB_SLAVE_DSRAM,

  // Write response channel
  input  wire                   BVALID_SLAVE_DSRAM,
  output reg                    BREADY_SLAVE_DSRAM,
  input  wire [1:0]             BRESP_SLAVE_DSRAM,

  // Read address channel
  output reg                    ARVALID_SLAVE_DSRAM,
  input  wire                   ARREADY_SLAVE_DSRAM,
  output reg  [addrwidth-1:0]   ARADDR_SLAVE_DSRAM,
  output reg  [2:0]             ARPROT_SLAVE_DSRAM,

  // Read data channel
  input  wire                   RVALID_SLAVE_DSRAM,
  output reg                    RREADY_SLAVE_DSRAM,
  input  wire [datawidth-1:0]   RDATA_SLAVE_DSRAM,
  input  wire [1:0]             RRESP_SLAVE_DSRAM,

  /* Xbar <=> UART */
  // Write address channel
  output reg                    AWVALID_SLAVE_UART,
  input  wire                   AWREADY_SLAVE_UART,
  output reg  [addrwidth-1:0]   AWADDR_SLAVE_UART,
  output reg  [2:0]             AWPROT_SLAVE_UART,

  // Write data channel
  output reg                    WVALID_SLAVE_UART,
  input  wire                   WREADY_SLAVE_UART,
  output reg  [datawidth-1:0]   WDATA_SLAVE_UART,
  output reg  [datawidth/8-1:0] WSTRB_SLAVE_UART,

  // Write response channel
  input  wire                   BVALID_SLAVE_UART,
  output reg                    BREADY_SLAVE_UART,
  input  wire [1:0]             BRESP_SLAVE_UART,

  // Read address channel
  output reg                    ARVALID_SLAVE_UART,
  input  wire                   ARREADY_SLAVE_UART,
  output reg  [addrwidth-1:0]   ARADDR_SLAVE_UART,
  output reg  [2:0]             ARPROT_SLAVE_UART,

  // Read data channel
  input  wire                   RVALID_SLAVE_UART,
  output reg                    RREADY_SLAVE_UART,
  input  wire [datawidth-1:0]   RDATA_SLAVE_UART,
  input  wire [1:0]             RRESP_SLAVE_UART,

  /* Xbar <=> CLINT */
  // Write address channel
  output reg                    AWVALID_SLAVE_CLINT,
  input  wire                   AWREADY_SLAVE_CLINT,
  output reg  [addrwidth-1:0]   AWADDR_SLAVE_CLINT,
  output reg  [2:0]             AWPROT_SLAVE_CLINT,

  // Write data channel
  output reg                    WVALID_SLAVE_CLINT,
  input  wire                   WREADY_SLAVE_CLINT,
  output reg  [datawidth-1:0]   WDATA_SLAVE_CLINT,
  output reg  [datawidth/8-1:0] WSTRB_SLAVE_CLINT,

  // Write response channel
  input  wire                   BVALID_SLAVE_CLINT,
  output reg                    BREADY_SLAVE_CLINT,
  input  wire [1:0]             BRESP_SLAVE_CLINT,

  // Read address channel
  output reg                    ARVALID_SLAVE_CLINT,
  input  wire                   ARREADY_SLAVE_CLINT,
  output reg  [addrwidth-1:0]   ARADDR_SLAVE_CLINT,
  output reg  [2:0]             ARPROT_SLAVE_CLINT,

  // Read data channel
  input  wire                   RVALID_SLAVE_CLINT,
  output reg                    RREADY_SLAVE_CLINT,
  input  wire [datawidth-1:0]   RDATA_SLAVE_CLINT,
  input  wire [1:0]             RRESP_SLAVE_CLINT
);
  // parameter define
  localparam IDEL   = 5'b00001;
  localparam DSRAM  = 5'b00010;
  localparam UART   = 5'b00100;
  localparam CLINT  = 5'b01000;
  localparam ERROR  = 5'b10000;  // TODO：处理地址不属于任何一个地址空间的情况

  // reg define
  reg [4:0] curr_status;
  reg [4:0] next_status;

  // wire define
  wire dsram_done = (BVALID_SLAVE_DSRAM && BREADY_SLAVE_DSRAM) || (RVALID_SLAVE_DSRAM && RREADY_SLAVE_DSRAM);
  wire uart_done  = (BVALID_SLAVE_UART && BREADY_SLAVE_UART) || (RVALID_SLAVE_UART && RREADY_SLAVE_UART);
  wire clint_done = (BVALID_SLAVE_CLINT && BREADY_SLAVE_CLINT) || (RVALID_SLAVE_CLINT && RREADY_SLAVE_CLINT);

  // status machine
  always @(posedge ACLK) begin
    if (ARESETn == 0) curr_status <= IDEL;
    else curr_status <= next_status;
  end
  // status transfer
  always @(*) begin
    case (curr_status)
      IDEL:
        if (AWVALID_SLAVE_1 && AWADDR_SLAVE_1 >= 32'h1000_0000 && AWADDR_SLAVE_1 <= 32'h1000_0fff)
          next_status = UART;
        else if (ARVALID_SLAVE_1 && ARADDR_SLAVE_1 >= 32'h1000_0000 && ARADDR_SLAVE_1 <= 32'h1000_0fff)
          next_status = UART;  // TODO: UART does not implement read function
        else if (AWVALID_SLAVE_1 && AWADDR_SLAVE_1 >= 32'h8000_0000 && AWADDR_SLAVE_1 <= 32'h80ff_ffff)
          next_status = DSRAM;
        else if (ARVALID_SLAVE_1 && ARADDR_SLAVE_1 >= 32'h8000_0000 && ARADDR_SLAVE_1 <= 32'h80ff_ffff)
          next_status = DSRAM;
        else if (AWVALID_SLAVE_1 && AWADDR_SLAVE_1 >= 32'ha000_0048 && AWADDR_SLAVE_1 <= 32'ha000_004c)
          next_status = CLINT; // TODO: CLINT does not implement write function
        else if (ARVALID_SLAVE_1 && ARADDR_SLAVE_1 >= 32'ha000_0048 && ARADDR_SLAVE_1 <= 32'ha000_004c)
          next_status = CLINT;
        else
          next_status = IDEL;

      DSRAM:
        if (dsram_done) next_status = IDEL;
        else next_status = DSRAM;

      UART:
        if (uart_done) next_status = IDEL;
        else next_status = UART;

      CLINT:
        if (clint_done) next_status = IDEL;
        else next_status = CLINT;

      default:
        next_status = IDEL;
    endcase
  end

  // Xbar => UART
  always @(*) begin
    if (curr_status == UART) begin
      // Write address channel
      AWVALID_SLAVE_UART = AWVALID_SLAVE_1;
      AWADDR_SLAVE_UART  = AWADDR_SLAVE_1;
      AWPROT_SLAVE_UART  = AWPROT_SLAVE_1;
      // Write data channel
      WVALID_SLAVE_UART  = WVALID_SLAVE_1;
      WDATA_SLAVE_UART   = WDATA_SLAVE_1;
      WSTRB_SLAVE_UART   = WSTRB_SLAVE_1;
      // Write response channel
      BREADY_SLAVE_UART  = BREADY_SLAVE_1;
      // Read address channel
      ARVALID_SLAVE_UART = ARVALID_SLAVE_1;
      ARADDR_SLAVE_UART  = ARADDR_SLAVE_1;
      ARPROT_SLAVE_UART  = ARPROT_SLAVE_1;
      // Read data channel
      RREADY_SLAVE_UART  = RREADY_SLAVE_1;
    end
    else begin
      // Write address channel
      AWVALID_SLAVE_UART = 1'b0;
      AWADDR_SLAVE_UART  = 0;
      AWPROT_SLAVE_UART  = 3'h0;
      // Write data channel
      WVALID_SLAVE_UART  = 1'b0;
      WDATA_SLAVE_UART   = 0;
      WSTRB_SLAVE_UART   = 0;
      // Write response channel
      BREADY_SLAVE_UART  = 1'b0;
      // Read address channel
      ARVALID_SLAVE_UART = 1'b0;
      ARADDR_SLAVE_UART  = 0;
      ARPROT_SLAVE_UART  = 3'h0;
      // Read data channel
      RREADY_SLAVE_UART  = 1'b0;
    end
  end

  // Xbar => DSRAM
  always @(*) begin
    if (curr_status == DSRAM) begin
      // Write address channel
      AWVALID_SLAVE_DSRAM = AWVALID_SLAVE_1;
      AWADDR_SLAVE_DSRAM  = AWADDR_SLAVE_1;
      AWPROT_SLAVE_DSRAM  = AWPROT_SLAVE_1;
      // Write data channel
      WVALID_SLAVE_DSRAM  = WVALID_SLAVE_1;
      WDATA_SLAVE_DSRAM   = WDATA_SLAVE_1;
      WSTRB_SLAVE_DSRAM   = WSTRB_SLAVE_1;
      // Write response channel
      BREADY_SLAVE_DSRAM  = BREADY_SLAVE_1;
      // Read address channel
      ARVALID_SLAVE_DSRAM = ARVALID_SLAVE_1;
      ARADDR_SLAVE_DSRAM  = ARADDR_SLAVE_1;
      ARPROT_SLAVE_DSRAM  = ARPROT_SLAVE_1;
      // Read data channel
      RREADY_SLAVE_DSRAM  = RREADY_SLAVE_1;
    end
    else begin
      // Write address channel
      AWVALID_SLAVE_DSRAM = 1'b0;
      AWADDR_SLAVE_DSRAM  = 0;
      AWPROT_SLAVE_DSRAM  = 3'h0;
      // Write data channel
      WVALID_SLAVE_DSRAM  = 1'b0;
      WDATA_SLAVE_DSRAM   = 0;
      WSTRB_SLAVE_DSRAM   = 0;
      // Write response channel
      BREADY_SLAVE_DSRAM  = 1'b0;
      // Read address channel
      ARVALID_SLAVE_DSRAM = 1'b0;
      ARADDR_SLAVE_DSRAM  = 0;
      ARPROT_SLAVE_DSRAM  = 3'h0;
      // Read data channel
      RREADY_SLAVE_DSRAM  = 1'b0;
    end
  end

  // Xbar => CLINT
  always @(*) begin
    if (curr_status == CLINT) begin
      // Write address channel
      AWVALID_SLAVE_CLINT = AWVALID_SLAVE_1;
      AWADDR_SLAVE_CLINT  = AWADDR_SLAVE_1;
      AWPROT_SLAVE_CLINT  = AWPROT_SLAVE_1;
      // Write data channel
      WVALID_SLAVE_CLINT  = WVALID_SLAVE_1;
      WDATA_SLAVE_CLINT   = WDATA_SLAVE_1;
      WSTRB_SLAVE_CLINT   = WSTRB_SLAVE_1;
      // Write response channel
      BREADY_SLAVE_CLINT  = BREADY_SLAVE_1;
      // Read address channel
      ARVALID_SLAVE_CLINT = ARVALID_SLAVE_1;
      ARADDR_SLAVE_CLINT  = ARADDR_SLAVE_1;
      ARPROT_SLAVE_CLINT  = ARPROT_SLAVE_1;
      // Read data channel
      RREADY_SLAVE_CLINT  = RREADY_SLAVE_1;
    end
    else begin
      // Write address channel
      AWVALID_SLAVE_CLINT = 1'b0;
      AWADDR_SLAVE_CLINT  = 0;
      AWPROT_SLAVE_CLINT  = 3'h0;
      // Write data channel
      WVALID_SLAVE_CLINT  = 1'b0;
      WDATA_SLAVE_CLINT   = 0;
      WSTRB_SLAVE_CLINT   = 0;
      // Write response channel
      BREADY_SLAVE_CLINT  = 1'b0;
      // Read address channel
      ARVALID_SLAVE_CLINT = 1'b0;
      ARADDR_SLAVE_CLINT  = 0;
      ARPROT_SLAVE_CLINT  = 3'h0;
      // Read data channel
      RREADY_SLAVE_CLINT  = 1'b0;
    end
  end

  // slaves(UART and DSRAM) => Xbar
  always @(*) begin
    case (curr_status)
      UART: begin
        // Write address channel
        AWREADY_SLAVE_1 = AWREADY_SLAVE_UART;
        // Write data channel
        WREADY_SLAVE_1  = WREADY_SLAVE_UART;
        // Write response channel
        BVALID_SLAVE_1  = BVALID_SLAVE_UART;
        BRESP_SLAVE_1   = BRESP_SLAVE_UART;
        // Read address channel
        ARREADY_SLAVE_1 = ARREADY_SLAVE_UART;
        // Read data channel
        RVALID_SLAVE_1  = RVALID_SLAVE_UART;
        RDATA_SLAVE_1   = RDATA_SLAVE_UART;
        RRESP_SLAVE_1   = RRESP_SLAVE_UART;
      end

      DSRAM: begin
        // Write address channel
        AWREADY_SLAVE_1 = AWREADY_SLAVE_DSRAM;
        // Write data channel
        WREADY_SLAVE_1  = WREADY_SLAVE_DSRAM;
        // Write response channel
        BVALID_SLAVE_1  = BVALID_SLAVE_DSRAM;
        BRESP_SLAVE_1   = BRESP_SLAVE_DSRAM;
        // Read address channel
        ARREADY_SLAVE_1 = ARREADY_SLAVE_DSRAM;
        // Read data channel
        RVALID_SLAVE_1  = RVALID_SLAVE_DSRAM;
        RDATA_SLAVE_1   = RDATA_SLAVE_DSRAM;
        RRESP_SLAVE_1   = RRESP_SLAVE_DSRAM;
      end

      CLINT: begin
        // Write address channel
        AWREADY_SLAVE_1 = AWREADY_SLAVE_CLINT;
        // Write data channel
        WREADY_SLAVE_1  = WREADY_SLAVE_CLINT;
        // Write response channel
        BVALID_SLAVE_1  = BVALID_SLAVE_CLINT;
        BRESP_SLAVE_1   = BRESP_SLAVE_CLINT;
        // Read address channel
        ARREADY_SLAVE_1 = ARREADY_SLAVE_CLINT;
        // Read data channel
        RVALID_SLAVE_1  = RVALID_SLAVE_CLINT;
        RDATA_SLAVE_1   = RDATA_SLAVE_CLINT;
        RRESP_SLAVE_1   = RRESP_SLAVE_CLINT;
      end

      default: begin
        // Write address channel
        AWREADY_SLAVE_1 = 1'b0;
        // Write data channel
        WREADY_SLAVE_1  = 1'b0;
        // Write response channel
        BVALID_SLAVE_1  = 1'b0;
        BRESP_SLAVE_1   = 2'b0;
        // Read address channel
        ARREADY_SLAVE_1 = 1'b0;
        // Read data channel
        RVALID_SLAVE_1  = 1'b0;
        RDATA_SLAVE_1   = 0;
        RRESP_SLAVE_1   = 2'b0;
      end
    endcase
  end

endmodule
